using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Cryptography;

/// <summary>
/// basic security class - free to use so long as you credit the author i.e. me
/// Matthew Dean mjdean@dmu.ac.uk
/// last update 17/8/2018
/// </summary>
namespace ClassLibrary
{

    public class clsSecurity
    {
        public class clsEmail
        {
            ///this class is internal to clsSecurity just to make it simpler to use
            ///you may want to consider using this definition to create your own class
            ///within your class library and then get rid of this definition
            public string Subject { get; internal set; }
            public string Recipient { get; internal set; }
            public string Sender { get; internal set; }
            public string Body { get; internal set; }

            public void SendEmail()
            {
                //sends an Email
                //this function won't work till all the code is activated
                System.Net.Mail.MailMessage Email = new System.Net.Mail.MailMessage();
                System.Net.Mail.SmtpClient Server = new System.Net.Mail.SmtpClient("mail.dmu.ac.uk");
                Email = new System.Net.Mail.MailMessage(this.Sender, this.Recipient, this.Subject, this.Body);
                //Server.Send(Email);
            }

        }

        //private data members
        //used to store the Email address of the currently authenticated user
        private string mUserEmail = "";
        //indicates if the user is admin or not
        private Boolean mAdmin = false;
        //records the number of failed login attempts
        private Int32 mAttempts;
        //stores the most recently sent Email message by the security system
        private clsEmail mEmailMessage;

        //constructor
        public clsSecurity()
        {
            //set attempts to zero
            mAttempts = 0;
        }


        public string SignUp(string Email, string Password, string ConfirmPassword, Boolean Admin)
        //public method allowing the user to sign up for an account
        {
            //var to store any errors
            string Message = "";
          
            //if the Email address isn't taken
            if (EmailTaken(Email) == false)
            {
                //if the two passwords match
                if (Password == ConfirmPassword)
                {
                    //check pasword complexity
                    Message = CheckPassword(Password);
                    //if complexity OK
                    if (Message == "")
                    {
                        //if the Email is valid
                        if (IsValidEmail(Email))
                        {
                            //get the hash of the plain text password
                            string HashPassword = GetHashString(Password + Email);
                            //add the record to the database
                            clsDataConnection DB = new clsDataConnection();
                            DB.AddParameter("@Email", Email.ToLower());
                            DB.AddParameter("@Password", HashPassword);
                            DB.AddParameter("@Admin", Admin);
                            DB.Execute("sproc_tblUser_Insert");
                            //if active not set to true then request Email activation
                            if (Admin == false)
                            {
                                
                            }
                            else
                            {
                                //set the return message
                                Message = "The account has been created.";
                            }
                        }
                        else
                        {
                            //set the return message
                            Message = "The e-mail address is not in a valid format";
                        }
                    }
                }
                //if the passwords do not match
                else
                {
                    //generate an error message
                    Message = "The passwords do not match.";
                }
            }
            else
            {
                //generate an error message
                Message = "The account already exists.";
            }
            //return the error message (if there is one)
            return Message;
        }

        private Boolean EmailTaken(string Email)
        {
            //tests to see if the Email address is taken
            //connect to the database and see if it there already
            clsDataConnection DB = new clsDataConnection();
            DB.AddParameter("@Email", Email.ToLower());
            DB.Execute("sproc_tblUser_FilterByEmail");
            //if one record found then it is already gone
            if (DB.Count == 1)
            {
                //return true
                return true;
            }
            else
            {
                //return false
                return false;
            }
        }

        private string GetHashString(string SomeText)
        {
            //generates a hash for storing secure data in the database
            if (SomeText != "")//if there is text to process
            {
                //create an instance of the hash generator
                SHA256Managed HashGenerator = new SHA256Managed();
                //var to store the final hash
                string HashString;
                //array to store the bytes of the orignal text
                byte[] TextBytes;
                //array to store the bytes of the new hash
                byte[] HashBytes;
                //convert the text in the string to an array of bytes
                TextBytes = System.Text.Encoding.UTF8.GetBytes(SomeText);
                //generate the hash based on the array of bytes
                HashBytes = HashGenerator.ComputeHash(TextBytes);
                //generate the hash string replacing blank characters with -
                HashString = BitConverter.ToString(HashBytes).Replace("-", "");
                return HashString;
            }
            else        //if there is nothing to process
            {
                //return a blank string
                return "";
            }
        }

        public string SignIn(string Email, string Password)
        {
            //signs in a user based on their Email and password
            //ver to store any error messages
            string Error = "";
            //if not all attempts are used up
            if (mAttempts < 3)
            {
                //convert the plain text password to a hash code
                //Password = GetHashString(Password + Email);
                //find the record matching the users Email address and password
                clsDataConnection User = new clsDataConnection();
                //add the parameters
                User.AddParameter("@Email", Email);
                User.AddParameter("@Password", Password);
                //execute the query
                User.Execute("sproc_tblUser_FilterByEmailAndPassword");
                //If there is only one record found then return true
                if (User.Count >= 1)
                {
                    //get the state of admin
                    mAdmin = Convert.ToBoolean(User.DataTable.Rows[0]["Admin"]);
                    //store the users Email address in the data member
                    mUserEmail = Email;
                }
                else //otherwise return false
                {
                    //increment the number of failed attempts
                    mAttempts++;
                    //return a message
                    Error = "Sign-in failed";
                }
            }
            else
            {
                //return a message
                Error = "There have been too many failed attempts please exit the application.";
            }
            //return any error messages
            return Error;
        }

        private Boolean SignInWithTempPW(string Email, string TempPW)
        {
            //used to log an account in using a temporary password

            //find the record matching the users Email address and password
            clsDataConnection UserAccount = new clsDataConnection();
            //add the parameters
            UserAccount.AddParameter("@Email", Email);
            UserAccount.AddParameter("@TempPW", TempPW);
            //execute the query
            UserAccount.Execute("sproc_tblAccount_FilterByEmailAndTempPW");
            //If there is only one record found then return true
            if (UserAccount.Count >= 1)
            {
                //set the users Email address
                mUserEmail = Email;
                //return true to indicate login ok
                return true;
            }
            else //otherwise return false
            {
                return false;
            }
        }
        public string UserEmail
        //allows access to Email address of the current user
        {
            get
            {
                //return the Email address
                return mUserEmail;
            }
        }

        public clsEmail EmailMessage
        //allows access to the last sent Email message
        {
            get
            {
                //retunr the message
                return mEmailMessage;
            }
        }

        public bool Authenticated
        //indicates if the current user is authenticated
        {
            get
            {
                //if there is a valid Email address
                if (mUserEmail != "")
                {
                    //return true
                    return true;
                }
                else
                {
                    //return false
                    return false;
                }
            }
        }


        private string CheckPassword(string Password)
        //used to check that the password meets requirments
        {
            string Err = "";
            //if the password is less then 7 characters
            if (Password.Length < 7)
            {
                Err = "Your password must be at least 7 characters ";
            }
            //if the password doesn't contain a number
            if (ContainsNumber(Password) == false)
            {
                Err = Err + "your password must contain a number ";
            }
            //return any errors
            return Err;
        }

        private bool IsValidEmail(string Email)
        {
            //tests to see if an Email is in a valid format
            try
            {
                //try to assign the eail to an instance of System.Net.Mail.MailAddress
                System.Net.Mail.MailAddress addr = new System.Net.Mail.MailAddress(Email);
                //if all ok return true
                return true;
            }
            catch
            {
                //else return false
                return false;
            }
        }

        private Boolean ContainsNumber(string Password)
        {
            //checks to see if a password contains a number
            //var to indicate found
            Boolean Found = false;
            //counter for loop
            int Counter = 0;
            //used to store a single character
            char AChar;
            //while found is false and char less than 9
            while (Found == false & Counter < 9)
            {
                //set temp to the value of Counter plus 48 to point at the numeric ascii codes
                int Temp = Counter + 48;
                //get the char value of the ascii code
                AChar = (char)Temp;
                //if the code is in the password
                if (Password.Contains(AChar) == true)
                {
                    //set found = true
                    Found = true;
                }
                else
                {
                    //otherwise keep looking
                    Counter += 1;
                }
            }
            //return the state of found
            return Found;
        }

        public string ChangePasswordWithTempPW(string Email, string TempPW, string Password, string ConfirmPassword)
        {
            //used to change a password using a temporary system generated password

            //var to store any errors
            string Message = "";
            //if the pw is blank then stop the process
            if (TempPW != "")
            {
                //if the account logs in OK
                if (SignInWithTempPW(Email, TempPW) == true)
                {
                    //if the two passwords match
                    if (Password == ConfirmPassword)
                    {
                        //check pasword complexity
                        Message = CheckPassword(Password);
                        //if complexity OK
                        if (Message == "")
                        {
                            //get the hash of the plain text password
                            string HashPassword = GetHashString(Password + Email);
                            //update the users passwod
                            clsDataConnection DB = new clsDataConnection();
                            DB.AddParameter("@Email", Email.ToLower());
                            DB.AddParameter("@Password", HashPassword);
                            DB.Execute("sproc_tblUser_UpdatePassword");
                            //return a message
                            Message = "The password has been changed";
                        }
                    }
                    //if the passwords do not match
                    else
                    {
                        //generate an error message
                        Message = "The passwords do not match.";
                    }
                }
                else
                {
                    //generate an error message
                    Message = "String not valid.";
                }
            }
            else
            {
                //generate an error message
                Message = "String not valid.";
            }
            //return the error message (if there is one)
            return Message;
        }

        private string GetTempPW()
        {
            //generates a temporary system generated password
            //var to store the password
            string TempPW = "";
            //create a new object for random numbers
            Random rnd = new Random();
            //loop 40 times 
            for (Int32 Count = 0; Count < 40; Count++)
            {
                //generate a random number between 0 and 9
                string ANo = rnd.Next(9).ToString();
                //concatenate the number to the TempPW string
                TempPW = TempPW + ANo;
            }
            //return the string
            return TempPW;
        }
        public string ChangePassword(string Email, string CurrentPassword, string Password, string ConfirmPassword)
        {
            //used to change a users password
            //var to store any errors
            string Message = "";
            //if the account logs in OK 
            if (SignIn(Email, CurrentPassword) == "" | mAdmin == true)
            {
                //if the two passwords match
                if (Password == ConfirmPassword)
                {
                    //check pasword complexity
                    Message = CheckPassword(Password);
                    if (Message == "")
                    {
                        //get the hash of the plain text password
                        string HashPassword = GetHashString(Password + Email);
                        //updat the password
                        clsDataConnection DB = new clsDataConnection();
                        DB.AddParameter("@Email", Email.ToLower());
                        DB.AddParameter("@Password", HashPassword);
                        DB.Execute("sproc_tblUser_UpdatePassword");
                        Message = "The password has been changed.";
                    }
                }
                //if the passwords do not match
                else
                {
                    //generate an error message
                    Message = "The passwords do not match.";
                }
            }
            else
            {
                //generate an error message
                Message = "The existing password was not correct.";
            }
            //return the error message (if there is one)
            return Message;
        }

        //private void SendActivationEmail(string Email)
        //{
        //    //sends an activation Email to the user when Email confirmation is required
        //    //generate a new Email message
        //    mEmailMessage = new clsEmail();
        //    //set the subject
        //    mEmailMessage.Subject = "Instructions for activating your account";
        //    //set the recipient
        //    mEmailMessage.Recipient = Email;
        //    //set the sender
        //    mEmailMessage.Sender = "noreply@dmu.ac.uk";
        //    //generate a temporary system password
        //    string TempPW = GetTempPW();
        //    //set the body of the Email
        //    mEmailMessage.Body = "<a href=ActivateAccount.aspx?Email=" + Email + "&TempPW=" + TempPW + ">Activate Account</a>";
        //    //connect to the database
        //    clsDataConnection DB = new clsDataConnection();
        //    DB.AddParameter("@Email", Email.ToLower());
        //    DB.AddParameter("@TempPW", TempPW);
        //    //update the temporary password
        //    DB.Execute("sproc_tblAccount_UpdateTempPW");
        //    //send the Email
        //    mEmailMessage.SendEmail();
        //}

        public void SignOut()
        {
            //signs the user out of the system
            //set Email to a blank string
            mUserEmail = "";
            //set admin to false
            mAdmin = false;
        }

        public string ReSet(string Email)
        {
            //used to generate a re-set password Email message
            //string to store any message
            String Message = "";
            //if the Email exists
            if (EmailTaken(Email) == true)
            {
                //generate a new Email message
                mEmailMessage = new clsEmail();
                mEmailMessage.Subject = "Instructions for re-setting your password";
                mEmailMessage.Recipient = Email;
                mEmailMessage.Sender = "noreply@dmu.ac.uk";
                //get the temp password
                string TempPW = GetTempPW();
                mEmailMessage.Body = "<a href=ChangePassword.aspx?Email=" + Email + "&TempPW=" + TempPW + ">Re Set Password</a>";
                //updat the temp password in the database
                clsDataConnection DB = new clsDataConnection();
                DB.AddParameter("@Email", Email.ToLower());
                DB.AddParameter("@TempPW", TempPW);
                DB.Execute("sproc_tblAccount_UpdateTempPW");
                //send the Email
                mEmailMessage.SendEmail();
                //set the return message
                Message = "An Email has been sent to your acccount with instructions on how to re-set your password.";
            }
            else
            {
                //send error
                Message = "Account not found";
            }
            //return any messages
            return Message;
        }

        public Boolean Admin
        {
            //used to flag if the user is admin or not
            get
            {
                //return state of data member
                return mAdmin;
            }
        }

        public string ActivateAccount(string Email, string TempPW)
        {
            //used to activate a web generated account to verify the Email address
            //var to store any errors
            string Message = "";
            //if there is a temp pasword
            if (TempPW != "")
            {
                //if it is possible to login using temp pw
                if (SignInWithTempPW(Email, TempPW) == true)
                {
                    if (Message == "")
                    {
                        //Activate the account
                        clsDataConnection DB = new clsDataConnection();
                        DB.AddParameter("@Email", Email.ToLower());
                        DB.Execute("sproc_tblAccount_Activate");
                        Message = "The account has been activated";
                    }
                }
                else
                {
                    //generate an error
                    Message = "The was a problem activating the account";
                }
            }
            else
            {
                //generate an error
                Message = "Invalid string";
            }
            //return the error message (if there is one)
            return Message;
        }
    }
}